import numpy as np
import pandas as pd


def sliding_window_polyfit_smooth(x, y, window_size=7, degree=3):
    """
    使用滑动窗口进行三次多项式拟合平滑

    参数:
        x: x坐标数组
        y: y坐标数组
        window_size: 窗口大小，默认7
        degree: 多项式次数，默认3（三次）

    返回:
        y_smooth: 平滑后的y值
    """
    n = len(y)
    y_smooth = np.zeros(n)

    half_window = window_size // 2

    for i in range(n):
        # 确定窗口边界
        start_idx = max(0, i - half_window)
        end_idx = min(n, i + half_window + 1)

        # 如果窗口太小，使用最小可用窗口
        if end_idx - start_idx < degree + 1:
            if i < half_window:
                start_idx = 0
                end_idx = min(n, degree + 1)
            else:
                end_idx = n
                start_idx = max(0, n - degree - 1)

        # 提取窗口数据
        x_window = x[start_idx:end_idx]
        y_window = y[start_idx:end_idx]

        # 三次多项式拟合
        try:
            coeffs = np.polyfit(x_window, y_window, degree)
            poly_func = np.poly1d(coeffs)
            y_smooth[i] = poly_func(x[i])
        except:
            # 如果拟合失败，使用原始值
            y_smooth[i] = y[i]

    return y_smooth


def smooth_and_recalculate(df, window_size=7):
    """
    对原始数据进行平滑处理并重新计算所有参数
    """
    # 确保数据排序
    df = df.sort_values('井斜深').reset_index(drop=True)

    # 提取原始数据
    x = df['井斜深'].values
    inclination_original = df['井斜角'].values
    azimuth_original = df['方位角'].values

    # 使用滑动窗口三次多项式平滑
    inclination_smooth = sliding_window_polyfit_smooth(x, inclination_original, window_size)
    azimuth_smooth = sliding_window_polyfit_smooth(x, azimuth_original, window_size)

    # 创建新的DataFrame用于平滑后的计算
    df_smooth = df.copy()
    df_smooth['井斜角'] = inclination_smooth
    df_smooth['方位角'] = azimuth_smooth

    # 重置其他列
    df_smooth['视位移'] = 0.0
    df_smooth['北移'] = 0.0
    df_smooth['东移'] = 0.0
    df_smooth['狗腿角'] = 0.0
    df_smooth['狗腿度'] = 0.0

    # 使用平滑后的数据重新计算
    return calculate_displacement(df_smooth)



def calculate_displacement(df):
    """
    计算视位移、北移、东移、狗腿角和狗腿度
    """
    # 确保数据按井斜深排序
    df = df.sort_values('井斜深').reset_index(drop=True)

    # 初始化结果列
    df['视位移'] = 0.0
    df['北移'] = 0.0
    df['东移'] = 0.0
    df['狗腿角'] = 0.0
    df['狗腿度'] = 0.0

    # 计算视位移（从第二行开始）
    for i in range(1, len(df)):
        A_prev = df.loc[i - 1, '井斜深']
        A_curr = df.loc[i, '井斜深']
        B_prev = df.loc[i - 1, '井斜角']
        B_curr = df.loc[i, '井斜角']
        E_prev = df.loc[i - 1, '视位移']

        # 角度转弧度
        angle_diff_rad = (B_curr - B_prev) * np.pi / 180
        avg_angle_rad = (B_prev + B_curr) / 2 * np.pi / 180

        # 计算视位移
        df.loc[i, '视位移'] = (E_prev +
                               (1 - (angle_diff_rad ** 2 / 24)) *
                               (A_curr - A_prev) *
                               np.sin(avg_angle_rad))

    # 计算北移、东移、狗腿角和狗腿度
    for i in range(1, len(df)):
        A_prev = df.loc[i - 1, '井斜深']
        A_curr = df.loc[i, '井斜深']
        B_prev = df.loc[i - 1, '井斜角']
        B_curr = df.loc[i, '井斜角']
        C_prev = df.loc[i - 1, '方位角']
        C_curr = df.loc[i, '方位角']
        F_prev = df.loc[i - 1, '北移']
        G_prev = df.loc[i - 1, '东移']

        # 计算狗腿角 - 修正版本
        B_prev_rad = np.radians(B_prev)
        B_curr_rad = np.radians(B_curr)
        C_diff_rad = np.radians(C_curr - C_prev)

        # 狗腿角计算公式
        cos_dogleg = (np.cos(B_prev_rad) * np.cos(B_curr_rad) +
                      np.sin(B_prev_rad) * np.sin(B_curr_rad) * np.cos(C_diff_rad))

        cos_dogleg = np.clip(cos_dogleg, -1.0, 1.0)
        dogleg_angle_rad = np.arccos(cos_dogleg)
        dogleg_angle_deg = np.degrees(dogleg_angle_rad)

        df.loc[i, '狗腿角'] = dogleg_angle_deg

        # 计算狗腿度 - 修正版本（注意：通常狗腿度是每30米的狗腿角）
        md_difference = A_curr - A_prev
        if md_difference > 0:
            # 如果是每米狗腿度，直接除；如果是每30米，乘30
            dogleg_severity = dogleg_angle_deg / md_difference * 30  # 每30米狗腿度
        else:
            dogleg_severity = 0

        df.loc[i, '狗腿度'] = dogleg_severity

        # 计算北移和东移
        B_diff_rad = np.radians(B_curr - B_prev)
        C_diff_rad = np.radians(C_curr - C_prev)
        B_avg_rad = np.radians((B_prev + B_curr) / 2)
        C_avg_rad = np.radians((C_prev + C_curr) / 2)

        correction_factor = 1 - (B_diff_rad ** 2 + C_diff_rad ** 2) / 24

        df.loc[i, '北移'] = (F_prev +
                             correction_factor *
                             (A_curr - A_prev) *
                             np.sin(B_avg_rad) *
                             np.cos(C_avg_rad))

        df.loc[i, '东移'] = (G_prev +
                             correction_factor *
                             (A_curr - A_prev) *
                             np.sin(B_avg_rad) *
                             np.sin(C_avg_rad))

    return df


def calculate_kadian_point(model):
    """计算卡点深度， 并展示 3D 图"""
    if model is None:
        return
    # 使用公式计算卡点深度: H = (F × E × ΔL) / P
    # 注意单位转换:
    # F - cm², E - GPa, ΔL - cm, P - kgf
    # 需要转换为国际单位制进行计算
    # 转换为国际单位
    si_data = model.to_si_units()
    # 截面积: m²
    area_m2 = si_data["cross_sectional_area"]
    # 弹性模量: Pa
    modulus_pa = si_data["elastic_modulus"]
    # 拉力: N
    force_n = si_data["pulling_force"]
    # 伸长量: m
    elongation = si_data["collar_inner_outer_diameter"]
    # 计算卡点深度 (m)
    # H = (F × E × ΔL) / P
    numerator = area_m2 * modulus_pa * elongation
    stuck_point_depth = numerator / force_n
    # 显示计算过程
    return stuck_point_depth
        # calculation_text = (
        #     f"计算过程:\n"
        #     f"截面积 F = {model.cross_sectional_area} cm² = {area_m2:.6e} m²\n"
        #     f"弹性模量 E = {model.elastic_modulus} GPa = {modulus_pa:.6e} Pa\n"
        #     f"伸长量 ΔL = {model.elongation} cm = {elongation:.6e} m\n"
        #     f"拉力 P = {model.pulling_force} kgf = {force_n:.6e} N\n"
        #     f"分子 F × E × ΔL = {numerator:.6e} N·m\n"
        #     f"卡点深度 H = (F × E × ΔL) / P = {stuck_point_depth:.2f} m"
        # )
        # self.ui.label_jsgc.setText(calculation_text)
        # self.ui.label_jsjg.setText(f"卡点深度：{stuck_point_depth:.2f}")
        # self.on_show_3d(stuck_point_depth, self.ui.widget_draw_kdjs, self.ui.tab_2)


def calculate_zhonghe_point(model):
    if model is None:
        return
    si_data = model.to_si_units()
    # TODO 中和点深度计算未知
    depth = 200
    return depth
    # self.ui.label_zhd_jsjg.setText(f"中和点深度：{depth:.2f}")
    # self.on_show_3d(depth, self.ui.widget_draw_zhdjs, self.ui.tab_3)


def calculate_mozuli(model, df_origin_data, df_zgsj):
    """
    摩阻力计算
    :param model: 输入框值集合
    :param df_origin_data: 井轨迹表格
    :param df_zgsj: 钻杆设计表格
    :return df_result: 计算结果表格
    """
    if model is None:
        return
    # 确保数据按井斜深排序
    df_origin_data = df_origin_data.sort_values('井斜深').reset_index(drop=True)
    # 初始化结果列
    df_origin_data['摩阻力（KN）'] = 0.0
    # 使用公式计算摩阻力:
    # 摩阻力Ff = 摩擦系数 * 9.81 * Lp * 名义重量 * 浮力系数Kf * sin(井斜角θ / 180 * π) + 下一行的摩阻力值
    # 浮力系数Kf = 1 - (修井液密度 / 钢材密度)
    # Lp 为管柱在直井段中的有效长度，具体取值方法例如：0--25米计算一次，有效长度就为25米，25--75米计算一次，有效长度就为50米，以此类推。
    # 需要转换为国际单位制进行计算
    # 转换为国际单位
    # si_data = model.to_si_units()
    si_data = model.to_dict()
    # 摩擦系数 从输入框中获取
    friction_coefficient = si_data["friction_coefficient"]
    # 修井液密度 从输入框中获取
    workover_fluid_density = si_data["workover_fluid_density"]
    # 钢材密度 从输入框中获取
    steel_density = si_data["steel_density"]
    # 计算浮力系数Kf
    buoyancy_factor = 1 - (workover_fluid_density / steel_density)
    # 从井轨迹表格中获取井斜角θ
    # 从最后一行（最深处）开始向上计算
    for i in range(len(df_origin_data)-1, -1, -1):
        # 获取当前行的井斜深和井斜角
        md = df_origin_data.iloc[i]['井斜深']  # 井斜深
        inclination = df_origin_data.iloc[i]['井斜角']  # 井斜角
        # 计算Lp（有效长度）
        if i > 0:
            lp = df_origin_data.iloc[i]['井斜深'] - df_origin_data.iloc[i-1]['井斜深']
        else:
            lp = 0
        # 根据当前井斜深确定所用钻杆的名义重量
        nominal_weight = 0.0
        # 遍历钻杆设计表格，找到对应深度范围的钻杆
        for idx, drill_pipe in df_zgsj.iterrows():
            # 使用"自"和"至"列确定深度范围
            try:
                # 尝试获取深度范围
                depth_from = drill_pipe.get('自')
                depth_to = drill_pipe.get('至')
                
                # 如果"自"和"至"列有值，则检查深度是否在范围内
                if depth_from is not None and depth_to is not None and depth_from != "" and depth_to != "":
                    depth_from = float(depth_from)
                    depth_to = float(depth_to)
                    # 检查当前深度是否在该钻杆的深度范围内
                    if depth_from <= md <= depth_to:
                        nominal_weight = drill_pipe['名义重量kg/m']
                        break
                else:
                    # 如果没有深度范围信息，使用第一个有效的名义重量
                    if '名义重量kg/m' in drill_pipe and nominal_weight == 0.0:
                        nominal_weight = drill_pipe['名义重量kg/m']
            except (ValueError, KeyError):
                # 数据转换失败时跳过该行
                continue
        # 计算摩阻力:
        # 摩阻力Ff = 摩擦系数 * 9.81 * Lp * 名义重量 * 浮力系数Kf * sin(井斜角θ / 180 * π) + 下一行的摩阻力值
        friction_force = (friction_coefficient * 9.81 * lp * float(nominal_weight) *
                         buoyancy_factor * np.sin(inclination / 180 * np.pi))
        # 加上下一行的摩阻力值
        if i < len(df_origin_data) - 1:
            friction_force += df_origin_data.iloc[i+1]['摩阻力（KN）']
        # 将计算结果存入当前行，保留3位小数
        df_origin_data.loc[df_origin_data.index[i], '摩阻力（KN）'] = round(friction_force, 3)
    return df_origin_data


def calculate_zhouxiangli(model, df_mozuli_data, df_zgsj):
    """
    计算轴向力
    :param model: 输入框值集合 (DrillingParameters)
    :param df_mozuli_data: 包含摩阻力计算结果的数据框
    :param df_zgsj: 钻杆设计表格
    :return: 包含轴向力计算结果的数据框
    """
    # 使用公式计算轴向力：
    # 有效轴向力 = 9.81 * (井深 - 井垂深) * 名义重量 * 浮力系数Kf - 摩阻力
    # 井深从输入框中获取
    
    if model is None or df_mozuli_data is None:
        return None
    
    # 确保数据按井斜深排序
    df = df_mozuli_data.sort_values('井斜深').reset_index(drop=True)
    
    # 初始化结果列
    df['轴向力（N）'] = 0.0
    
    # 转换为国际单位制
    # si_data = model.to_si_units()
    si_data = model.to_dict()
    
    # 从输入框中获取所需参数
    # 井深 (well_depth)
    well_depth = si_data["well_depth"]

    # 钻井液密度 (kg/m³)
    workover_fluid_density = si_data["workover_fluid_density"]
    
    # 钢材密度 (kg/m³)
    steel_density = si_data["steel_density"]
    
    # 计算浮力系数
    fb = 1 - (workover_fluid_density / steel_density)
    
    # 从最后一行（最深处）开始向上计算轴向力
    for i in range(len(df)):
        # 获取当前行的摩阻力
        friction_force = df.iloc[i]['摩阻力（KN）']

        # 获取当前行的井斜深
        md = df.iloc[i]['井斜深']

        # 获取当前行的井垂深
        tvd = df.iloc[i]['井垂深']
        
        # 根据当前井斜深确定所用钻杆的名义重量和尺寸
        nominal_weight = 0.0
        
        # 遍历钻杆设计表格，找到对应深度范围的钻杆
        for idx, drill_pipe in df_zgsj.iterrows():
            try:
                # 尝试获取深度范围
                depth_from = drill_pipe.get('自')
                depth_to = drill_pipe.get('至')
                
                # 如果"自"和"至"列有值，则检查深度是否在范围内
                if depth_from is not None and depth_to is not None and depth_from != "" and depth_to != "":
                    depth_from = float(depth_from)
                    depth_to = float(depth_to)
                    # 检查当前深度是否在该钻杆的深度范围内
                    if depth_from <= md <= depth_to:
                        nominal_weight = float(drill_pipe.get('名义重量kg/m', 0.0))
                        break
                else:
                    # 如果没有深度范围信息，使用第一个有效的数据
                    if nominal_weight == 0.0 and '名义重量kg/m' in drill_pipe:
                        nominal_weight = float(drill_pipe.get('名义重量kg/m', 0.0))
            except (ValueError, KeyError):
                # 数据转换失败时跳过该行
                continue
        
        # 计算轴向力
        # 有效轴向力 = 9.81 * (井深 - 井垂深) * 名义重量 * 浮力系数Kf - 摩阻力
        axial_force = 9.81 * (well_depth - tvd) * nominal_weight * fb - friction_force
        # axial_force = axial_force * 1e-3  # KN
        
        # 将计算结果存入当前行
        df.loc[df.index[i], '轴向力（N）'] = round(axial_force, 3)
    
    return df


def calculate_wanquyingli(model, df_data, df_zgsj):
    """
    计算弯曲应力
    :param model: 输入框值集合 (DrillingParameters)
    :param df_data: 包含井斜角、井垂深等数据的DataFrame
    :param df_zgsj: 钻杆设计表格
    :return: 包含弯曲应力计算结果的DataFrame
    """
    # 使用公式计算弯曲应力：
    # 弯曲应力 = (弹性模量 * 管柱外径 * 10^-3 * Δα) / (2 * ΔL)
    # Δα = (α1 - α2) * π / 180
    # α1、α2为测段两点的井斜角
    # ΔL为测段的井垂深差
    
    if model is None or df_data is None:
        return None
    
    # 确保数据按井斜深排序
    df = df_data.sort_values('井斜深').reset_index(drop=True)
    
    # 初始化结果列
    df['弯曲应力（Pa）'] = 0.0
    
    # 转换为国际单位制
    # si_data = model.to_si_units()
    si_data = model.to_dict()
    
    # 从输入框中获取弹性模量
    elastic_modulus = si_data["elastic_modulus"]  # 单位: N/m²
    
    # 从第一行开始计算（除了最后一行）
    for i in range(len(df) - 1):
        # 获取当前点和下一点的数据
        md1 = df.iloc[i]['井斜深']
        md2 = df.iloc[i+1]['井斜深']
        incl1 = df.iloc[i]['井斜角']
        incl2 = df.iloc[i+1]['井斜角']
        tvd1 = df.iloc[i]['井垂深']
        tvd2 = df.iloc[i+1]['井垂深']
        
        # 计算Δα = (α1 - α2) * π / 180
        delta_alpha = abs(incl1 - incl2) * np.pi / 180
        
        # 计算ΔL为测段的井垂深差
        delta_L = abs(tvd2 - tvd1)
        
        # 避免除零错误
        if delta_L == 0:
            continue
            
        # 根据当前井斜深确定所用钻杆的外径
        outer_diameter = 0.0  # 外径 (mm)
        
        # 遍历钻杆设计表格，找到对应深度范围的钻杆
        for idx, drill_pipe in df_zgsj.iterrows():
            try:
                # 尝试获取深度范围
                depth_from = drill_pipe.get('自')
                depth_to = drill_pipe.get('至')
                
                # 检查当前深度是否在该钻杆的深度范围内
                if depth_from is not None and depth_to is not None and depth_from != "" and depth_to != "":
                    depth_from = float(depth_from)
                    depth_to = float(depth_to)
                    if depth_from <= md1 <= depth_to:
                        outer_diameter = float(drill_pipe.get('外径/mm', 0.0))
                        break
                else:
                    # 如果没有深度范围信息，使用第一个有效的外径
                    if outer_diameter == 0.0 and '外径/mm' in drill_pipe:
                        outer_diameter = float(drill_pipe.get('外径/mm', 0.0))
            except (ValueError, KeyError):
                # 数据转换失败时跳过该行
                continue
        
        # 计算弯曲应力
        # 弯曲应力 = (弹性模量 * 管柱外径 * 10^-3 * Δα) / (2 * ΔL)
        # 需要将Pa转换为MPa (除以10^6)
        bending_stress = (elastic_modulus * outer_diameter * 1e-3 * delta_alpha) / (2 * delta_L)
        # bending_stress_mpa = bending_stress / 1e6  # 转换为MPa
        
        # 将计算结果存入当前行
        df.loc[df.index[i], '弯曲应力（Pa）'] = round(bending_stress, 3)
    
    # 最后一行的弯曲应力设为0
    if len(df) > 0:
        df.loc[df.index[len(df)-1], '弯曲应力（Pa）'] = 0.0
    
    return df


def calculate_ququ_status(model, df_data, df_zgsj):
    """
    屈曲分析
    :param model: 输入框值集合 (DrillingParameters)
    :param df_data: 包含井斜角、井垂深等数据的DataFrame
    :param df_zgsj: 钻杆设计表格
    :return: 包含屈曲状态分析结果的DataFrame
    """

    if model is None or df_data is None:
        return None

    # 确保数据按井斜深排序
    df = df_data.sort_values('井斜深').reset_index(drop=True)

    # 初始化结果列
    df['正弦屈曲临界载荷（N）'] = 0.0
    df['螺旋屈曲临界载荷（N）'] = 0.0
    df['屈曲状态'] = ""

    # 转换为国际单位制
    # si_data = model.to_si_units()
    si_data = model.to_dict()

    # 从输入框中获取所需参数
    elastic_modulus = si_data["elastic_modulus"]  # 弹性模量 (Pa)
    slip_length = si_data["slip_length"]  # 井筒内径 (mm)

    # 对每个深度点进行屈曲分析
    for i in range(len(df)):
        # 获取当前行的井斜深
        md = df.iloc[i]['井斜深']

        # 获取轴向力（如果存在）
        axial_force = 0.0
        if '轴向力（N）' in df.columns:
            axial_force = abs(df.iloc[i]['轴向力（N）'])  # 取绝对值
        else:
            # 如果没有轴向力数据，则无法进行屈曲分析
            df.loc[df.index[i], '屈曲状态'] = "无轴向力数据"
            continue

        # 根据当前井斜深确定所用钻杆的尺寸
        outer_diameter = 0.0  # 管柱外径 (mm)
        inner_diameter = 0.0  # 管柱内径 (mm)
        nominal_weight = 0.0  # 名义重量

        # 遍历钻杆设计表格，找到对应深度范围的钻杆
        for idx, drill_pipe in df_zgsj.iterrows():
            try:
                # 尝试获取深度范围
                depth_from = drill_pipe.get('自')
                depth_to = drill_pipe.get('至')

                # 检查当前深度是否在该钻杆的深度范围内
                if depth_from is not None and depth_to is not None and depth_from != "" and depth_to != "":
                    depth_from = float(depth_from)
                    depth_to = float(depth_to)
                    if depth_from <= md <= depth_to:
                        outer_diameter = float(drill_pipe.get('外径/mm', 0.0))
                        # 计算内径 = 外径 - 2 * 壁厚
                        wall_thickness = float(drill_pipe.get('壁厚/mm', 0.0))
                        inner_diameter = outer_diameter - 2 * wall_thickness
                        nominal_weight = float(drill_pipe.get('名义重量kg/m', 0.0))
                        break
                else:
                    # 如果没有深度范围信息，使用第一个有效的数据
                    if outer_diameter == 0.0 and '外径/mm' in drill_pipe:
                        outer_diameter = float(drill_pipe.get('外径/mm', 0.0))
                        wall_thickness = float(drill_pipe.get('壁厚/mm', 0.0))
                        inner_diameter = outer_diameter - 2 * wall_thickness
                        nominal_weight = float(drill_pipe.get('名义重量kg/m', 0.0))
            except (ValueError, KeyError):
                # 数据转换失败时跳过该行
                continue

        # 如果没有获取到钻杆尺寸信息
        if outer_diameter == 0.0:
            df.loc[df.index[i], '屈曲状态'] = "无钻杆尺寸数据"
            continue

        # 计算浮重
        workover_fluid_density = si_data.get("workover_fluid_density", 1000.0)  # 默认水密度
        steel_density = si_data.get("steel_density", 7850.0)  # 默认钢材密度
        buoyancy_factor = 1 - (workover_fluid_density / steel_density)
        tvd_length = df.iloc[i]['井垂深'] - (df.iloc[i-1]['井垂深'] if i > 0 else 0)  # 该段长度为当前行减去上一行的井垂深
        Wg = nominal_weight * 9.81 * tvd_length  # 空气中重（N）=名义重量×9.81×该段长度
        buoyancy_force = Wg * buoyancy_factor

        # 计算参数
        # I = π * (管柱外径^4 - 管柱内径^4) / 64 * 10^-12
        # 转换mm到m: 10^-12
        I = np.pi * (outer_diameter**4 - inner_diameter**4) / 64 * 1e-12

        # r = (井筒内径 - 管柱外径) / 2 * 10^-3
        # 转换mm到m: 10^-3
        r = (slip_length - outer_diameter) / 2 * 1e-3

        # 避免除零或负数开方错误
        if r <= 0:
            df.loc[df.index[i], '屈曲状态'] = "间隙过小"
            continue

        # 正弦屈曲临界载荷
        # 正弦屈曲临界载荷 = 2 * √(弹性模量 * I * Bf / r)
        try:
            sin_critical_load = 2 * np.sqrt(elastic_modulus * I * buoyancy_force / r)

            # 螺旋屈曲临界载荷
            # 螺旋屈曲临界载荷 = √(2 * 弹性模量 * I * Bf / r)
            spiral_critical_load = np.sqrt(2 * elastic_modulus * I * buoyancy_force / r)

            # 保存临界载荷值到DataFrame中（保留2位小数）
            df.loc[df.index[i], '正弦屈曲临界载荷（N）'] = round(sin_critical_load, 2)
            df.loc[df.index[i], '螺旋屈曲临界载荷（N）'] = round(spiral_critical_load, 2)

            # 判断屈曲状态
            # 轴向力F < 正弦屈曲临界载荷，屈曲状态显示为未屈曲状态
            # 正弦屈曲临界载荷 < 轴向力F < 螺旋屈曲临界载荷，屈曲状态显示为正弦屈曲
            # 螺旋屈曲临界载荷 < 轴向力F，屈曲状态显示为螺旋屈曲

            if axial_force < sin_critical_load:
                ququ_status = "未屈曲"
            elif sin_critical_load <= axial_force < spiral_critical_load:
                ququ_status = "正弦屈曲"
            elif axial_force >= spiral_critical_load:
                ququ_status = "螺旋屈曲"
            else:
                ququ_status = "未知状态"

            df.loc[df.index[i], '屈曲状态'] = ququ_status

        except (ValueError, ZeroDivisionError):
            df.loc[df.index[i], '屈曲状态'] = "计算错误"

    return df


def fuzhengqi_recommend(model, df_data, df_zgsj):
    """
    扶正器推荐
    :param model: 输入框值集合 (DrillingParameters)
    :param df_data: 包含井斜角等数据的DataFrame
    :param df_zgsj: 钻杆设计表格
    :return: 包含扶正器推荐结果的DataFrame
    """
    # 根据屈曲分析的结果表，只在屈曲状态为正弦屈曲的管段进行计算
    # L临界 = π * √((弹性模量 * I) / (q有效 * sin(θ)))
    # I = π * (管柱外径^4 - 管柱内径^4) / 64 * 10^-12
    # q = 名义重量 * 9.81
    # q有效 = q - 修井液密度 * 9.81 * π / 4(管柱外径^2 - 管柱内径^2)
    # θ为对应管段的井斜角，这里的井斜角取管段两端点井斜角的平均值，既θ = (θ1 + θ2) / 2
    # 扶正器间距 = L临界 * 0.9

    if model is None or df_data is None:
        return None

    # 确保数据按井斜深排序
    df = df_data.sort_values('井斜深').reset_index(drop=True)

    # 检查是否有屈曲状态列，如果没有则先进行屈曲分析
    if '曲屈状态' not in df.columns:
        df = calculate_ququ_status(model, df, df_zgsj)

    # 筛选出屈曲状态为正弦屈曲的管段
    sinusoidal_buckling_data = df[df['屈曲状态'] == '正弦屈曲']

    if len(sinusoidal_buckling_data) == 0:
        # 如果没有正弦屈曲的管段，返回空结果
        return pd.DataFrame(columns=['井斜角', '起始测深（m）', '终止测深（m）', '扶正器间距（m）', '第一只距工具面距离（m）'])

    # 转换为国际单位制
    # si_data = model.to_si_units()
    si_data = model.to_data_dict()

    # 从输入框中获取所需参数
    elastic_modulus = si_data["elastic_modulus"]  # 弹性模量 (Pa)
    workover_fluid_density = si_data["workover_fluid_density"]  # 修井液密度 (kg/m³)

    # 初始化结果列表
    results = []

    # 遍历钻杆设计表格中的每一行（每个管段）
    for idx, drill_pipe in df_zgsj.iterrows():
        try:
            # 获取管段的起始和终止深度
            depth_from = drill_pipe.get('自')
            depth_to = drill_pipe.get('至')

            # 检查深度范围是否有效
            if depth_from is None or depth_to is None or depth_from == "" or depth_to == "":
                continue

            depth_from = float(depth_from)
            depth_to = float(depth_to)

            # 获取该管段对应的数据点
            segment_data = sinusoidal_buckling_data[
                (sinusoidal_buckling_data['井斜深'] >= depth_from) &
                (sinusoidal_buckling_data['井斜深'] <= depth_to)
            ]

            if len(segment_data) == 0:
                continue  # 该管段没有正弦屈曲点

            # 获取管柱尺寸参数
            outer_diameter = float(drill_pipe.get('外径/mm', 0.0))  # 管柱外径 (mm)
            wall_thickness = float(drill_pipe.get('壁厚/mm', 0.0))  # 壁厚 (mm)
            nominal_weight = float(drill_pipe.get('名义重量kg/m', 0.0))  # 名义重量 (kg/m)

            # 计算内径
            inner_diameter = outer_diameter - 2 * wall_thickness  # 管柱内径 (mm)

            # 检查尺寸参数是否有效
            if outer_diameter <= 0 or nominal_weight <= 0:
                continue

            # 计算截面惯性矩 I
            # I = π * (管柱外径^4 - 管柱内径^4) / 64 * 10^-12
            # 转换mm到m: 10^-12
            I = np.pi * (outer_diameter**4 - inner_diameter**4) / 64 * 1e-12

            # 计算单位长度重量 q
            # q = 名义重量 * 9.81
            q = nominal_weight * 9.81

            # 计算有效单位长度重量 q有效
            # q有效 = q - 修井液密度 * 9.81 * π / 4(管柱外径^2 - 管柱内径^2)
            # 需要将mm转换为m: 10^-6
            q_effective = q - workover_fluid_density * 9.81 * np.pi / 4 * (
                (outer_diameter**2 - inner_diameter**2) * 1e-6)

            # 确保有效重量不为负或零
            if q_effective <= 0:
                q_effective = q  # 如果计算出负值，使用名义重量

            # 计算该管段的平均井斜角  TODO 待修改
            avg_inclination = segment_data['井斜角'].mean()

            # 将角度转换为弧度
            theta_rad = np.radians(avg_inclination)

            # 避免sin(θ)为0或负数
            if np.sin(theta_rad) <= 0:
                continue

            # 计算临界长度 L临界
            # L临界 = π * √((弹性模量 * I) / (q有效 * sin(θ)))
            try:
                L_critical = np.pi * np.sqrt((elastic_modulus * I) / (q_effective * np.sin(theta_rad)))
            except (ValueError, ZeroDivisionError):
                continue  # 计算失败则跳过该管段

            # 计算扶正器间距 = L临界 * 0.9
            fuzhengqi_spacing = L_critical * 0.9

            # 第一只距工具面距离通常取扶正器间距的一半
            first_distance = fuzhengqi_spacing / 2

            # 为该管段中的每个正弦屈曲点添加推荐结果
            for _, point in segment_data.iterrows():
                md = point['井斜深']
                results.append({
                    '井斜角': avg_inclination,
                    '起始测深（m）': depth_from,
                    '终止测深（m）': depth_to,
                    '扶正器间距（m）': round(fuzhengqi_spacing, 2),
                    '第一只距工具面距离（m）': round(first_distance, 2)
                })

        except (ValueError, KeyError, TypeError):
            # 数据转换或计算过程中出错，跳过该管段
            continue

    # 将结果转换为DataFrame
    if results:
        result_df = pd.DataFrame(results)
        return result_df
    else:
        # 如果没有有效结果，返回空的DataFrame
        return pd.DataFrame(columns=['井斜角', '起始测深（m）', '终止测深（m）', '扶正器间距（m）', '第一只距工具面距离（m）'])
